#include "mainwindow.h"
#include <QHBoxLayout>
#include <QFileDialog>
#include <QMessageBox>
#include <QPainter>
#include <opencv2/features2d.hpp>  // OpenCV特征检测（SIFT等）
#include <QMenuBar>
#include <QMenu>
#include <QAction>
// --------- ImageCanvas 类的实现 ---------
// 构造函数，继承自 QLabel，用于显示图片和鼠标选框
ImageCanvas::ImageCanvas(QWidget *parent)
        : QLabel(parent)
{
    setMouseTracking(true);         // 开启鼠标跟踪，用于鼠标移动事件
    setMinimumSize(400, 400);       // 设置最小尺寸，防止控件太小
    setStyleSheet("background-color:black;"); // 背景设为黑色
    setAlignment(Qt::AlignTop | Qt::AlignLeft); // 图像靠左上角显示
}

// 设置显示的图像（cv::Mat格式）
void ImageCanvas::setImage(const cv::Mat &mat) {
    if (mat.empty()) {              // 若空图像，则清空显示内容
        m_image = QImage();
        m_scale = 1.0;
        m_selectionRect = QRect(); // 清除选区矩形
        m_resultRect = QRect();    // 清除结果矩形
        update();                  // 触发重绘
        return;
    }
    cv::Mat rgb;
    if(mat.channels() == 3)        // 若是3通道BGR，转成RGB
        cv::cvtColor(mat, rgb, cv::COLOR_BGR2RGB);
    else if(mat.channels() == 1)   // 单通道灰度转3通道RGB
        cv::cvtColor(mat, rgb, cv::COLOR_GRAY2RGB);
    else
        rgb = mat;                 // 其他情况直接赋值

    // 计算控件大小与图片大小的缩放比例，保持图片完整显示
    QSize labelSize = size();
    double scaleX = double(labelSize.width()) / rgb.cols;
    double scaleY = double(labelSize.height()) / rgb.rows;
    m_scale = std::min(scaleX, scaleY); // 取最小比例

    // 按比例缩放图像
    cv::Mat resized;
    cv::resize(rgb, resized,
               cv::Size(int(rgb.cols * m_scale), int(rgb.rows * m_scale)));

    // 将 OpenCV Mat 转成 Qt QImage，注意deep copy
    m_image = QImage(resized.data, resized.cols, resized.rows, resized.step, QImage::Format_RGB888).copy();

    // 清除之前的框选区域
    m_selectionRect = QRect();
    m_resultRect = QRect();

    update();                      // 触发重绘
}

// 清除所有矩形框
void ImageCanvas::clearRectangles() {
    m_selectionRect = QRect();
    m_resultRect = QRect();
    update();
}
// 设置左图红色选区矩形框
void ImageCanvas::setSelectionRect(const QRect &rect) {
    m_selectionRect = rect;
    update();
}
// 设置右图蓝色结果矩形框
void ImageCanvas::setResultRect(const QRect &rect) {
    m_resultRect = rect;
    update();
}
// 获取当前选区矩形框
QRect ImageCanvas::getSelectionRect() const {
    return m_selectionRect;
}

// 鼠标按下事件，开始框选
void ImageCanvas::mousePressEvent(QMouseEvent *event) {
    if (m_image.isNull())          // 没有图片时不响应
        return;

    m_selecting = true;            // 标记开始选取
    m_startPoint = event->pos();   // 记录起点
    m_selectionRect = QRect(m_startPoint, QSize()); // 初始化矩形大小为0
    update();
}
// 鼠标移动事件，更新选区矩形
void ImageCanvas::mouseMoveEvent(QMouseEvent *event) {
    if (!m_selecting)              // 只有正在选取时才处理
        return;
    QPoint currentPoint = event->pos();
    m_selectionRect = QRect(m_startPoint, currentPoint).normalized(); // 生成规范化矩形（左上到右下）
    update();                     // 重绘
    emit selectionChanged(m_selectionRect); // 发出选区变化信号
}

// 鼠标释放事件，结束选取
void ImageCanvas::mouseReleaseEvent(QMouseEvent *event) {
    if (!m_selecting)
        return;
    m_selecting = false;           // 取消选取标记
    QPoint currentPoint = event->pos();
    m_selectionRect = QRect(m_startPoint, currentPoint).normalized(); // 规范化矩形
    update();
    emit selectionChanged(m_selectionRect); // 发送信号
}

// 重写绘制事件，绘制图片及矩形框
void ImageCanvas::paintEvent(QPaintEvent *event) {
    QLabel::paintEvent(event);     // 先调用基类绘制

    if (m_image.isNull())
        return;

    QPainter painter(this);
    // 计算图片绘制时左上角坐标，实现居中显示
    int x = (width() - m_image.width()) / 2;
    int y = (height() - m_image.height()) / 2;
    painter.drawImage(x, y, m_image);  // 画出图片

    // 画红色选区矩形框
    if (m_selectionRect.isValid()) {
        painter.setPen(QPen(Qt::red, 2));
        painter.drawRect(m_selectionRect);
    }
    // 画蓝色结果矩形框
    if (m_resultRect.isValid()) {
        painter.setPen(QPen(Qt::blue, 2));
        painter.drawRect(m_resultRect);
    }
}

// --------- MainWindow 类的实现 ---------
// 构造函数，创建布局及信号槽连接
MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent),
          m_leftCanvas(new ImageCanvas(this)),
          m_rightCanvas(new ImageCanvas(this)),
          m_textLabel(new QLabel(this))
{
    QWidget* central = new QWidget(this);
    setCentralWidget(central);
    QHBoxLayout* mainLayout = new QHBoxLayout(central); // 水平布局

    mainLayout->addWidget(m_leftCanvas, 4);  // 左图，权重3
    mainLayout->addWidget(m_rightCanvas, 4); // 右图，权重3

    m_textLabel->setMinimumWidth(300);       // 右侧文本显示宽度限制
    m_textLabel->setWordWrap(true);           // 自动换行
    m_textLabel->setStyleSheet("background-color:#f0f0f0; padding:10px;"); // 背景和内边距
    mainLayout->addWidget(m_textLabel, 2);   // 文本框权重2
    // 创建菜单栏 - 文件菜单
    QMenu* fileMenu = menuBar()->addMenu("文件");
    QAction* load1 = fileMenu->addAction("加载图1");
    QAction* load2 = fileMenu->addAction("加载图2");
    fileMenu->addSeparator();
    QAction* quit = fileMenu->addAction("退出");
    // 菜单动作连接槽函数
    connect(load1, &QAction::triggered, this, &MainWindow::loadImage1);
    connect(load2, &QAction::triggered, this, &MainWindow::loadImage2);
    connect(quit, &QAction::triggered, this, &MainWindow::close);

    // 连接左图选区变化信号，响应槽函数
    connect(m_leftCanvas, &ImageCanvas::selectionChanged, this, &MainWindow::onSelectionChanged);
    setWindowTitle("图像OCR区域对比工具（C++ Qt版，OCR未实现）");
    resize(1600, 900);
}

// 加载左侧图1
void MainWindow::loadImage1() {
    QString filename = QFileDialog::getOpenFileName(this, "选择图1");
    if (filename.isEmpty())          // 未选择文件则返回
        return;

    m_img1 = cv::imread(filename.toStdString());  // 使用OpenCV读取图像
    if (m_img1.empty()) {
        QMessageBox::warning(this, "错误", "无法加载图1");
        return;
    }

    m_leftCanvas->setImage(m_img1);   // 显示左图
    // 重置右图相关状态和显示
    m_img2.release();
    m_img2_aligned.release();
    m_H.release();
    m_rightCanvas->clearRectangles();
    m_rightCanvas->setImage(cv::Mat()); // 清空右图
    m_textLabel->clear();
}

// 加载右侧图2并尝试配准
void MainWindow::loadImage2() {
    if (m_img1.empty()) {             // 必须先加载图1
        QMessageBox::warning(this, "提示", "请先加载图1");
        return;
    }

    QString filename = QFileDialog::getOpenFileName(this, "选择图2");
    if (filename.isEmpty())
        return;

    m_img2 = cv::imread(filename.toStdString());
    if (m_img2.empty()) {
        QMessageBox::warning(this, "错误", "无法加载图2");
        return;
    }

    alignImages();                    // 执行图像配准
}

// 图像配准处理
void MainWindow::alignImages() {
    try {
        cv::Mat gray1, gray2;
        cv::cvtColor(m_img1, gray1, cv::COLOR_BGR2GRAY);  // 左图转灰度
        cv::cvtColor(m_img2, gray2, cv::COLOR_BGR2GRAY);  // 右图转灰度

        // 使用OpenCV内置SIFT创建特征检测器（OpenCV 4.4及以上）
        auto sift = cv::SIFT::create();
        std::vector<cv::KeyPoint> kp1, kp2;  // 关键点容器
        cv::Mat des1, des2;                  // 描述子矩阵
        sift->detectAndCompute(gray1, cv::noArray(), kp1, des1); // 左图提取特征
        sift->detectAndCompute(gray2, cv::noArray(), kp2, des2); // 右图提取特征
        // 使用FLANN匹配器进行特征匹配
        cv::FlannBasedMatcher matcher;
        std::vector<std::vector<cv::DMatch>> knnMatches;
        matcher.knnMatch(des1, des2, knnMatches, 2);  // KNN匹配，找两个最近邻
        // 通过 Lowe's ratio test 筛选优质匹配
        std::vector<cv::DMatch> goodMatches;
        for (auto& m : knnMatches) {
            if (m.size() == 2 && m[0].distance < 0.7 * m[1].distance)
                goodMatches.push_back(m[0]);
        }

        if (goodMatches.size() < 4) {       // 匹配点过少无法计算单应矩阵
            QMessageBox::warning(this, "错误", "配准失败，匹配点不足");
            return;
        }
        // 提取匹配点坐标用于计算单应矩阵
        std::vector<cv::Point2f> pts1, pts2;
        for (auto& m : goodMatches) {
            pts1.push_back(kp1[m.queryIdx].pt);    // 左图点
            pts2.push_back(kp2[m.trainIdx].pt);    // 右图点
        }
        // 计算单应矩阵(H)，将右图映射到左图
        m_H = cv::findHomography(pts2, pts1, cv::RANSAC);
        // 使用单应矩阵对右图进行透视变换配准，大小与左图一致
        cv::warpPerspective(m_img2, m_img2_aligned, m_H, m_img1.size());

        m_rightCanvas->setImage(m_img2_aligned);  // 显示配准后的右图
        m_rightCanvas->clearRectangles();          // 清除旧矩形框
        m_textLabel->setText("配准成功，已显示右侧配准图像。\n\n"
                             "左侧图像框选区域时，右侧将显示对应蓝色框。\n"
                             "OCR功能尚未实现。");

    } catch (const cv::Exception& e) {
        // 捕获OpenCV异常，弹窗提示
        QMessageBox::critical(this, "错误", QString("配准异常: %1").arg(e.what()));
    }
}
// 处理左图选区变化事件
void MainWindow::onSelectionChanged(const QRect &rect) {
    if (rect.isNull() || m_img1.empty() || m_img2_aligned.empty())
        return;
    // 根据左图缩放比例，将控件坐标转换为原始图像坐标
    double scale = m_leftCanvas->getScale();
    int x1 = int(rect.left() / scale);
    int y1 = int(rect.top() / scale);
    int x2 = int(rect.right() / scale);
    int y2 = int(rect.bottom() / scale);
    // 保证坐标在图像范围内
    x1 = std::clamp(x1, 0, m_img1.cols - 1);
    y1 = std::clamp(y1, 0, m_img1.rows - 1);
    x2 = std::clamp(x2, 0, m_img1.cols - 1);
    y2 = std::clamp(y2, 0, m_img1.rows - 1);
    m_currentSelection = QRect(QPoint(x1, y1), QPoint(x2, y2)).normalized();
    // 计算右图对应的矩形区域，乘以右图缩放比例映射到控件坐标
    double scaleRight = m_rightCanvas->getScale();

    QRect rightRect(
            int(m_currentSelection.left() * scaleRight),
            int(m_currentSelection.top() * scaleRight),
            int(m_currentSelection.width() * scaleRight),
            int(m_currentSelection.height() * scaleRight));
    // 设置左图红框与右图蓝框
    m_leftCanvas->setSelectionRect(rect);
    m_rightCanvas->setResultRect(rightRect);
    // 显示选区信息及提示
    m_textLabel->setText(QString("选中区域：(%1, %2) - (%3, %4)\n\nOCR功能未实现。")
                                 .arg(m_currentSelection.left()).arg(m_currentSelection.top())
                                 .arg(m_currentSelection.right()).arg(m_currentSelection.bottom()));
}
// 更新显示图像（如果需要）
void MainWindow::updateDisplay() {
    if (!m_img1.empty())
        m_leftCanvas->setImage(m_img1);

    if (!m_img2_aligned.empty())
        m_rightCanvas->setImage(m_img2_aligned);
}
